using System;
using System.Collections.Generic;
using UnityEngine;

public class TableTennisRemotePlayer : TableTennisPlayerBase, RemotePlayerMessageHandlers
{
	protected PhysicsBall ball;

	private bool isInServe;

	public float serveTossAnimSpeed = 2f;

	public float hitHeight = 0.98f;

	public float ballTossSpeed = 3.6f;

	public float minTimeToHit = 0.2f;

	private Animator cachedAnimator_;

	public float serveTimeOffsetFraction = 0.1f;

	protected StoreItemAdapter.ShoeAdapter shoe_;

	public Vector3 serveOffsetPosition = new Vector3(-0.0964f, -0.0163f, 0f);

	protected MagnusForceAndAirResistanceSquaredBallFlight symFlight = new MagnusForceAndAirResistanceSquaredBallFlight();

	private Vector3 serveCentralPosition = new Vector3(0.024f, 0f, 2.083f);

	private MStartServeToss lastServeToss = default(MStartServeToss);

	private float maxServeHeight = 2f;

	protected Animator cachedAnimator
	{
		get
		{
			if (cachedAnimator_ == null)
			{
				for (int i = 0; i < base.transform.childCount; i++)
				{
					Transform child = base.transform.GetChild(i);
					cachedAnimator_ = child.GetComponent<Animator>();
					if (cachedAnimator_ != null)
					{
						break;
					}
				}
			}
			return cachedAnimator_;
		}
	}

	public StoreItemAdapter.ShoeAdapter shoe
	{
		get
		{
			if (shoe_ == null)
			{
				shoe_ = new StoreItemAdapter.RemoteShoe(MatchParameters.InitParameters.multiplayerParams.remoteShoe.moveSpeed, MatchParameters.InitParameters.multiplayerParams.remoteShoe.jumpSpeed, MatchParameters.InitParameters.multiplayerParams.remoteShoe.shotsToMinSpeed, MatchParameters.InitParameters.multiplayerParams.remoteShoe.normalizedVisualSpeed, MatchParameters.InitParameters.multiplayerParams.roomType);
			}
			return shoe_;
		}
	}

	public float moveSpeed
	{
		get
		{
			return shoe.GetSpeed(base.match.gameType.numShotsInPoint);
		}
	}

	public float jumpSpeed
	{
		get
		{
			return shoe.GetJumpSpeed(base.match.gameType.numShotsInPoint);
		}
	}

	public bool ballFlyingTowardsMe { get; protected set; }

	public float ServeTimeOffsetFraction()
	{
		return serveTimeOffsetFraction;
	}

	public override float GetSkillPoints(PlayerStatsManager.SkillType skillType)
	{
		return MatchParameters.InitParameters.multiplayerParams.remoteSkills.GetSkillValue((int)skillType) * MatchParameters.InitParameters.skillPointsScale + MatchParameters.InitParameters.skillPointsOffset;
	}

	public void SetPosition(MRemotePosInfo racketPos)
	{
	}

	public void HitBall(PhysicsBall ball, MHitBall hitBall)
	{
		GGDebug.DebugLog("Not implemented for this player");
	}

	public HitBallParamsPhysicsFlight HitBall(PhysicsBall ball, MHitBallAvatar hitBall)
	{
		if (!hitBall.isServe && !ball.flightSequence.isActive && !ball.isBallInGame)
		{
			return default(HitBallParamsPhysicsFlight);
		}
		KineticElementHit firstElementOfType = base.kineticPlan.GetFirstElementOfType<KineticElementHit>();
		if (firstElementOfType != null)
		{
			firstElementOfType.trackFlight = null;
		}
		if (isInServe)
		{
			ball.HitOnServe();
		}
		isInServe = false;
		this.ball = ball;
		this.ball.position = hitBall.ballPosition;
		this.ball.transform.parent = null;
		if (hitBall.missWide)
		{
			base.match.ui.ballFollower.SetColor(Color.red);
		}
		else
		{
			HitParamsDB.TimingRange timingRange = ScriptableObjectSingleton<HitParamsDB>.instance.timing.GetTimingRange(hitBall.hitTiming, hitBall.skillDifference, hitBall.maxTiming);
			base.match.ui.ballFollower.SetColor(timingRange.color);
		}
		FlightParams flightParams = default(FlightParams);
		flightParams.position = hitBall.ballPosition;
		flightParams.velocity = hitBall.ballVelocity;
		flightParams.angularVelocity = hitBall.ballAngularVelocity;
		flightParams.optionalNormal = hitBall.ballOptionalNormal;
		FlightSimulator instance = Singleton<FlightSimulator>.instance;
		FlightSimulator.SequenceSimulationParams sim = FlightSimulator.SequenceSimulationParams.DefaultRecordFlightParams(flightParams, symFlight);
		sim.deltaTime = hitBall.simDeltaTime;
		instance.FillFlightSequence(ball.flightSequence, sim);
		ball.flightSequence.Reset();
		ball.isServeBall = hitBall.isServe;
		ball.playerThatHitBall = this;
		HitParameters hitParams = default(HitParameters);
		hitParams.attackPoints = hitBall.attackPoints;
		hitParams.opponentDefensePoints = hitBall.opponentDefensePoints;
		hitParams.reductionFactor = hitBall.reductionFactor;
		hitParams.quality = hitBall.quality;
		ball.hitParams = hitParams;
		ball = null;
		HitParamsDB.Hit hit = ScriptableObjectSingleton<HitParamsDB>.instance.GetHit((HitParamsDB.HitType)hitBall.hitType);
		HitParamsDB.HitRange range = hit.GetRange(hitBall.power);
		HitParamsDB.QualityRange qualityRange = range.GetQualityRange(hitBall.quality);
		PlaySound(qualityRange.GetClip());
		movePlan.MoveToPosition(base.playerPosition, hitBall.player1EndPosition, null, hitBall.timeToGetToPosition, hitBall.hitFlightTime);
		KineticServeHitElement firstElementOfType2 = base.kineticPlan.GetFirstElementOfType<KineticServeHitElement>();
		if (firstElementOfType2 != null)
		{
			firstElementOfType2.canMove = false;
		}
		MovePlanParams movePlanParams = default(MovePlanParams);
		movePlanParams.startPosition = hitBall.player1StartPosition;
		movePlanParams.desiredPosition = hitBall.player1EndPosition;
		movePlanParams.timeToGetToPosition = hitBall.timeToGetToPosition;
		movePlanParams.hitFlightTime = hitBall.hitFlightTime;
		HitBallParamsPhysicsFlight result = default(HitBallParamsPhysicsFlight);
		result.initFlightParams = flightParams;
		result.hitParams = hitParams;
		result.simDeltaTime = symDeltaTime;
		result.isServe = hitBall.isServe;
		result.hitType = hitBall.hitType;
		result.power = hitBall.power;
		result.movePlan = movePlanParams;
		result.missWide = hitBall.missWide;
		return result;
	}

	public void SwipeToHit(MStartSwipe swipe, PhysicsBall ball)
	{
		base.kineticPlan.CancelAllPendingElements();
		if (swipe.isBallNull)
		{
			animController.AddHitAnimation(base.kineticPlan, -swipe.ballNullSwipeGesture, this);
			return;
		}
		CancelMovePlan();
		animController.maxMoveSpeed = moveSpeed;
		animController.maxJumpSpeed = 20f;
		if (swipe.isChosenHitPlanNull)
		{
			base.kineticPlan.CancelAllPendingElements();
			animController.AddHitAnimation(base.kineticPlan, base.match.ball, this);
			return;
		}
		try
		{
			HitAnimationsStateEnum hitAnimationsStateEnum = (HitAnimationsStateEnum)Enum.Parse(typeof(HitAnimationsStateEnum), swipe.hitName);
		}
		catch (ArgumentException)
		{
			HitAnimationsStateEnum hitAnimationsStateEnum = HitAnimationsStateEnum.ForehandTopspin1;
		}
		PlayerAnimationsController.HitPlan hitPlan = new PlayerAnimationsController.HitPlan();
		hitPlan.hitParams = new PlayerAnimationsController.HitAnimationParams();
		hitPlan.hitParams.type = (HitParamsDB.HitType)swipe.hitType;
		base.match.OnPlayerChosenHitPlan(this, hitPlan);
		animController.AddHitAnimation(base.kineticPlan, swipe, symDeltaTime, ball, null, this);
	}

	private void GoToHit(PhysicsBall ball, bool shouldHit, bool comeClose = false)
	{
		base.kineticPlan.CancelAllPendingElements();
		if (base.kineticPlan.indexOfElementOfType<KineticElementHit>() >= 0)
		{
			return;
		}
		this.ball = ball;
		if (ball == null)
		{
			animController.AddHitAnimation(base.kineticPlan, Vector3.zero, this);
			return;
		}
		CancelMovePlan();
		comeClose = !shouldHit;
		animController.maxMoveSpeed = moveSpeed;
		if (shouldHit)
		{
			animController.maxJumpSpeed = 20f;
		}
		else
		{
			animController.maxJumpSpeed = jumpSpeed;
		}
		List<PlayerAnimationsController.HitPlan> hitPlans = animController.CreateHitPlans(Vector3.zero, base.kineticPlan, ball, symDeltaTime, comeClose);
		PlayerAnimationsController.HitPlan hitPlan = animController.ChooseHitPlan(hitPlans);
		if (hitPlan == null)
		{
			if (shouldHit)
			{
				base.kineticPlan.CancelAllPendingElements();
				animController.AddHitAnimation(base.kineticPlan, base.match.ball, this);
			}
			return;
		}
		base.match.OnPlayerChosenHitPlan(this, hitPlan);
		float num = hitPlan.hitFlightTime - ball.flightSequence.flightTime;
		if (shouldHit)
		{
			animController.AddHitAnimation(base.kineticPlan, hitPlan, ball, null);
			float num2 = Vector3.Distance(hitPlan.hitPoint.maxPosition.OnGround(), base.playerPosition.OnGround()) / (hitPlan.hitFlightTime - ball.flightSequence.flightTime);
			Vector3 vector = (hitPlan.hitPoint.maxPosition - hitPlan.startState.rotation * hitPlan.hitInfo.hitPosition).OnGround();
			float num3 = (vector - hitPlan.startState.position).OnGround().magnitude / (hitPlan.hitFlightTime - ball.flightSequence.flightTime);
			GGDebug.DebugLog("### Time Till hit " + hitPlan.hitFlightTime);
		}
		else
		{
			Quaternion rotationForHitPlan = animController.GetRotationForHitPlan(hitPlan);
			HitAnimationInfo hitInfo = hitPlan.hitInfo;
			Vector3 desiredPosition = (hitPlan.hitPoint.maxPosition - rotationForHitPlan * hitPlan.hitInfo.hitPosition).OnGround();
			float timeToGetToPosition = Mathf.Max(Vector3Ex.HorizontalDistance(base.playerPosition, hitPlan.startAnimPosition) / moveSpeed, ball.flightSequence.GetStageThatStartsFromTableSide(base.playerPosition.z).startTime * 0.5f);
			movePlan.MoveToPosition(base.playerPosition, desiredPosition, hitInfo, timeToGetToPosition, hitPlan.hitFlightTime);
		}
		if (num > 0f)
		{
			float num4 = Vector3Ex.HorizontalDistance(base.playerPosition, hitPlan.startAnimPosition) / num;
			if (num4 > 0.6f && UnityEngine.Random.Range(0, 2) == 0)
			{
				this.WaitAndExecute(UnityEngine.Random.Range(0.5f, 0.9f) * num, PlaySqueekSound);
			}
		}
	}

	private void PlaySqueekSound()
	{
		PlaySound(base.match.ball.shoeSqueek, UnityEngine.Random.Range(0.5f, 1f));
	}

	public void DoStartServe(MStartServeToss serveToss)
	{
		base.match.cameraController.OnActionWaitDone();
		lastServeToss = serveToss;
		base.kineticPlan.CancelAll(false);
		animController.TossBallInAir(base.kineticPlan, serveTossAnimSpeed, OnStartServeThrowDelegate);
	}

	private void OnStartServeThrowDelegate(KineticServeHitElement hit)
	{
		ball.transform.parent = null;
		float num = Mathf.Sqrt(-2f * Physics.gravity.y * (maxServeHeight - ball.position.y));
		HitAnimationInfo orCreateServeHitAnimationByEnum = ScriptableObjectSingleton<KineticAnimationInfoDB>.instance.GetOrCreateServeHitAnimationByEnum(ServeHitAnimationsStateEnum.ServeHit);
		hitHeight = orCreateServeHitAnimationByEnum.hitPosition.y;
		MathEx.NullPoints nullPoints = MathEx.FindParabolaNullPoints((0f - Physics.gravity.y) / 2f, 0f - num, hitHeight - ball.position.y);
		float max = nullPoints.Max;
		Vector3 vector = base.playerPosition + base.kineticPlan.cachedTransform.rotation * orCreateServeHitAnimationByEnum.hitPosition.OnGround();
		vector.y = hitHeight;
		Vector3 velocity = (vector - ball.position).OnGround() / max;
		velocity.y = num;
		ball.StartServe(velocity, lastServeToss.framerateScale);
		base.kineticPlan.CancelAllPendingElements();
		animController.FillServeToss(ServeHitAnimationsStateEnum.ServeHit, base.kineticPlan, null, max / lastServeToss.framerateScale - base.kineticPlan.activeElement.durationTillEnd, false);
	}

	private void Update()
	{
		KineticElementHit firstElementOfType = base.kineticPlan.GetFirstElementOfType<KineticElementHit>();
		if (firstElementOfType != null && firstElementOfType.trackFlight != null)
		{
			CancelMovePlan();
		}
		if (firstElementOfType != null || !movePlan.isActive)
		{
			return;
		}
		movePlan.ellapsedTime += GGGameTime.deltaTime;
		Vector3 vector = Vector3.Lerp(movePlan.positionWhenStartMove, movePlan.desiredPosition, movePlan.ellapsedTime / movePlan.timeToGetToPosition);
		Vector3 vector2 = (vector - base.playerPosition) / GGGameTime.deltaTime;
		cachedAnimator.SetBool("ServeCrouch", false);
		cachedAnimator.SetFloat("SpeedX", vector2.x);
		cachedAnimator.SetFloat("SpeedY", vector2.z);
		base.playerPosition = vector;
		if (movePlan.ellapsedTime > movePlan.timeToGetToPosition)
		{
			CancelMovePlan();
			cachedAnimator.SetFloat("SpeedX", 0f);
			cachedAnimator.SetFloat("SpeedY", 0f);
			if (movePlan.onMoveDone != null)
			{
				movePlan.onMoveDone();
			}
		}
	}

	public override void OpponentChosenHitPlan(TableTennisPlayerBase opponent, PlayerAnimationsController.HitPlan hitPlan)
	{
		base.OpponentChosenHitPlan(opponent, hitPlan);
		if (hitPlan != null && base.match.ball.isBallInGame && hitPlan.hitParams.type == HitParamsDB.HitType.Smash)
		{
			PrepareForSmash();
		}
	}

	public void PrepareForSmash()
	{
		Vector3 vector = Vector3.forward * Table.halfLength * 2f * base.tableSide;
		float num = Vector3Ex.HorizontalDistance(base.playerPosition, vector) / moveSpeed;
		movePlan.MoveToPosition(base.playerPosition, vector, null, num, num);
	}

	public override bool IsInServe()
	{
		return isInServe;
	}

	public override void Init(int teamTag, int tableSide, TableTennisMatchController match, bool canAlwaysHit)
	{
		base.Init(teamTag, tableSide, match, canAlwaysHit);
	}

	public override void BallRemovedFromGame()
	{
		ball = null;
		isInServe = false;
		CancelMovePlan();
		base.kineticPlan.CancelAll(false);
	}

	public override void StartServe(PhysicsBall ball)
	{
		GGDebug.DebugLog("Remote Start Serve");
		CancelMovePlan();
		base.kineticPlan.CancelAllPendingElements();
		this.ball = ball;
		this.ball.ParentToObject(serveHand);
		this.ball.localPosition = serveOffsetPosition;
		ballFlyingTowardsMe = false;
		isInServe = true;
		base.playerPosition = serveCentralPosition;
		base.kineticPlan.CancelAll();
		StartMoveIntoServe();
	}

	public override void AwaitServe()
	{
		ball = null;
		isInServe = false;
		ball = null;
		base.kineticPlan.CancelAll();
		base.playerPosition = serveCentralPosition;
		CancelMovePlan();
	}

	public override void ReceiveBall(PhysicsBall ball)
	{
		this.ball = ball;
		ballFlyingTowardsMe = true;
		base.kineticPlan.CancelAllPendingElements();
		CancelMovePlan();
		GoToHit(ball, false);
	}

	private void CancelMovePlan()
	{
		movePlan.isActive = false;
		cachedAnimator.SetFloat("SpeedX", 0f);
		cachedAnimator.SetFloat("SpeedY", 0f);
	}

	private void StartMoveIntoServe()
	{
		CancelMovePlan();
		base.kineticPlan.CancelAll(false);
		animController.FillStartServePlan(serveCentralPosition, base.kineticPlan);
	}
}
